﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="MainPageViewModel.cs" company="Microsoft">
//   Microsoft
// </copyright>
// <summary>
//   Main Page ViewModel class
// </summary>
// Copyright (c) Microsoft Corporation. All rights reserved.
// 
// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, 
// EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES 
// OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
// ----------------------------------------------------------------------------------
// The example companies, organizations, products, domain names,
// e-mail addresses, logos, people, places, and events depicted
// herein are fictitious.  No association with any real company,
// organization, product, domain name, email address, logo, person,
// places, or events is intended or should be inferred.
// --------------------------------------------------------------------------------------------------------------------

namespace MySemanticSearch.ViewModel
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.ComponentModel;
    using System.Globalization;
    using System.IO;
    using System.Linq;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Input;

    using MySemanticSearch.Commands;
    using MySemanticSearch.Model;
    using MySemanticSearch.Service;
    using MySemanticSearch.TagCloud;
    using MySemanticSearch.View;
    using MySemanticSearch.Web.Model;

    /// <summary>
    /// Main Page ViewModel class
    /// </summary>
    public class MainPageViewModel : INotifyPropertyChanged
    {
        #region Private Fields
        /// <summary>
        /// Constant slider maximum value
        /// </summary>
        private const int SliderTopMaxValue = 50;

        /// <summary>
        /// List of selected documents
        /// </summary>
        private List<string> selectedDocuments;

        /// <summary>
        /// MainPage view
        /// </summary>
        private IView view;

        /// <summary>
        /// Collection of filter tags Keyphrase selected
        /// </summary>
        private ObservableCollection<KeyPhrase> tagListSelectedItems;

        /// <summary>
        /// Private property tags clouds for the canvas that holds the tags for the selected documents
        /// </summary>
        private ObservableCollection<KeyPhrase> currentTagCloudTags;

        /// <summary>
        /// Private property tags clouds for the canvas
        /// </summary>
        private ObservableCollection<KeyPhrase> fullTagCloudTags;

        /// <summary>
        /// Selected SQL Instance
        /// </summary>
        private string selectedSqlInstance;

        /// <summary>
        /// Slider value
        /// </summary>
        private double sliderValue;

        /// <summary>
        /// Temporal slider maximum value
        /// </summary>
        private int sliderMax;

        /// <summary>
        /// Private field for the tag amount status label
        /// </summary>
        private string amountStatus;

        /// <summary>
        /// Private field for the target status label
        /// </summary>
        private string targetStatus;

        /// <summary>
        /// Selected document for the document list
        /// </summary>
        private Document selectedDocument;

        /// <summary>
        /// Search box string
        /// </summary>
        private string txtSearch;

        /// <summary>
        /// Full text search value
        /// </summary>
        private string searchText;

        /// <summary>
        /// Filter tag Keyphrase selected
        /// </summary>
        private KeyPhrase tagListSelectedItem;

        /// <summary>
        /// Filter tag Keyphrase by selected row on Tag Grid
        /// </summary>
        private KeyPhrase tagGridSelectedItem;

        /// <summary>
        /// Search property
        /// </summary>
        private string selectedSearchDocumentProperty;

        /// <summary>
        /// Selected filetable database
        /// </summary>
        private Database selectedDatabase;

        /// <summary>
        /// Selected filetable
        /// </summary>
        private string selectedFileTable;

        /// <summary>
        /// Boolean indicating whether the SQL instance dropdown is enabled
        /// </summary>
        private bool isSqlInstanceDropDownEnabled;

        /// <summary>
        /// Boolean indicating whether the Database dropdown is enabled
        /// </summary>
        private bool isDatabasesDropDownEnabled;

        /// <summary>
        /// Boolean indicating whether the FileTables dropdown is enabled
        /// </summary>
        private bool isFileTablesDropDownEnabled;

        /// <summary>
        /// Indicates whether a document in the datagrid has been selected
        /// </summary>
        private bool isDocumentSelected;

        /// <summary>
        /// Indicates if the find button is visible
        /// </summary>
        private string isFindButtonVisible = "Collapsed";

        /// <summary>
        /// Selected document in realted list
        /// </summary>
        private Document selectedRelatedDocument;

        /// <summary>
        /// Service that exposes methods to query the RIA service
        /// </summary>
        private IDomainService domainService;

        /// <summary>
        /// Stream after opening a file
        /// </summary>
        private Stream saveFileStream;

        #endregion

        /// <summary>
        /// Initializes a new instance of the <see cref="MainPageViewModel"/> class.
        /// </summary>
        /// <param name="mainPage">
        /// The main page.
        /// </param>
        public MainPageViewModel(IView mainPage)
        {
            this.Initialize(mainPage, new DomainService());
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="MainPageViewModel"/> class.
        /// </summary>
        /// <param name="mainPage">
        /// The main page.
        /// </param>
        /// <param name="service">
        /// The service.
        /// </param>
        public MainPageViewModel(IView mainPage, IDomainService service)
        {
            this.Initialize(mainPage, service);
        }

        /// <summary>
        /// Property changed handler
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        #region Commands
        /// <summary>
        /// Gets FileInformationCommand.
        /// </summary>
        public ICommand FileInformationCommand
        {
            get
            {
                return new RelayCommand(this.ShowFileInformationDialog);
            }
        }

        /// <summary>
        /// Gets UpdateDocumentsCommand.
        /// </summary>
        public ICommand UpdateDocumentsCommand
        {
            get
            {
                return new RelayCommand(this.UpdateDocumentList);
            }
        }

        /// <summary>
        /// Gets InstanceCommand.
        /// </summary>
        public ICommand InstanceCommand
        {
            get
            {
                return new RelayCommand(this.ShowConfigureInstanceDialog);
            }
        }

        /// <summary>
        /// Gets FileTableCommand.
        /// </summary>
        public ICommand FileTableCommand
        {
            get
            {
                return new RelayCommand(this.ShowCreateFileTableDialog);
            }
        }

        /// <summary>
        /// Gets DeleteDocumentCommand.
        /// </summary>
        public ICommand DeleteDocumentCommand
        {
            get
            {
                return new RelayCommand(this.DeleteDocument);
            }
        }

        /// <summary>
        /// Gets UpdateDocumentListCommand.
        /// </summary>
        public ICommand UpdateDocumentListCommand
        {
            get
            {
                return new RelayCommand(this.UpdateSelectedDocument);
            }
        }

        /// <summary>
        /// Gets AddDocumentCommand.
        /// </summary>
        public ICommand AddDocumentCommand
        {
            get
            {
                return new RelayCommand(this.UploadFile);
            }
        }

        /// <summary>
        /// Gets DownloadDocumentCommand.
        /// </summary>
        public ICommand DownloadDocumentCommand
        {
            get
            {
                return new RelayCommand(this.DownloadDocument);
            }
        }

        /// <summary>
        /// Gets SelectionChanged.
        /// </summary>
        public ICommand DocumentSelectionChanged
        {
            get
            {
                return new RelayParameterizedCommand(this.OnSelectionChanged);
            }
        }

        /// <summary>
        /// Gets TagFilterSelectionCommand.
        /// </summary>
        public ICommand TagFilterSelectionChanged
        {
            get
            {
                return new RelayParameterizedCommand(this.OnTagFilterSelectionChanged);
            }
        }

        #endregion

        #region Properties
        /// <summary>
        /// Gets or sets SelectedSQLInstance.
        /// </summary>
        public string SelectedSqlInstance
        {
            get
            {
                return this.selectedSqlInstance;
            }

            set
            {
                this.selectedSqlInstance = value;
                this.NotifyPropertyChanged("SelectedSqlInstance");
                if (String.IsNullOrEmpty(value))
                {
                    this.IsSqlInstanceDropDownEnabled = false;
                    return;
                }

                this.IsSqlInstanceDropDownEnabled = true;
                this.domainService.GetDatabaseNameList(this.OnGetDatabaseNameListCompleted, this.SelectedSqlInstance, true);
            }
        }

        /// <summary>
        /// Gets or sets SelectedDatabase.
        /// </summary>
        public Database SelectedDatabase
        {
            get
            {
                return this.selectedDatabase;
            }

            set
            {
                this.selectedDatabase = value;
                this.NotifyPropertyChanged("SelectedDatabase");
                if (null == value)
                {
                    this.IsDatabasesDropDownEnabled = false;
                    return;
                }

                this.IsDatabasesDropDownEnabled = true;
                this.domainService.GetFileTableList(this.OnGetFiletableListComplete, this.SelectedSqlInstance, this.selectedDatabase.Name);
            }
        }

        /// <summary>
        /// Gets or sets SelectedFileTable.
        /// </summary>
        public string SelectedFileTable
        {
            get
            {
                return this.selectedFileTable;
            }

            set
            {
                this.selectedFileTable = value;
                this.NotifyPropertyChanged("SelectedFileTable");
                if (String.IsNullOrEmpty(value))
                {
                    this.IsFileTablesDropDownEnabled = false;
                    return;
                }

                this.IsFileTablesDropDownEnabled = true;
                this.UpdateDocuments();
                this.GetTags();
                this.domainService.GetSearchProperties(this.OnGetSearchPropertiesCompleted, this.SelectedSqlInstance, this.SelectedDatabase.Name, this.SelectedFileTable);
            }
        }

        /// <summary>
        /// Gets SqlInstances.
        /// </summary>
        public ObservableCollection<string> SqlInstances { get; private set; }

        /// <summary>
        /// Gets or sets a value indicating whether IsSqlInstanceDropDownEnabled.
        /// </summary>
        public bool IsSqlInstanceDropDownEnabled
        {
            get
            {
                return this.isSqlInstanceDropDownEnabled;
            }

            set
            {
                this.isSqlInstanceDropDownEnabled = value;
                this.NotifyPropertyChanged("IsSqlInstanceDropDownEnabled");
            }
        }

        /// <summary>
        /// Gets Databases.
        /// </summary>
        public ObservableCollection<Database> Databases { get; private set; }

        /// <summary>
        /// Gets FileTables.
        /// </summary>
        public ObservableCollection<string> FileTables { get; private set; }

        /// <summary>
        /// Gets or sets a value indicating whether IsDatabasesDropDownEnabled.
        /// </summary>
        public bool IsDatabasesDropDownEnabled
        {
            get
            {
                return this.isDatabasesDropDownEnabled;
            }

            set
            {
                this.isDatabasesDropDownEnabled = value;
                this.NotifyPropertyChanged("IsDatabasesDropDownEnabled");
            }
        }

        /// <summary>
        /// Gets or sets SelectedRelatedDocument.
        /// </summary>
        public Document SelectedRelatedDocument
        {
            get
            {
                return this.selectedRelatedDocument;
            }

            set
            {
                this.selectedRelatedDocument = value;
                this.NotifyPropertyChanged("SelectedRelatedDocument");
                if (value != null)
                {
                    this.IsFindButtonVisible = "Visible";
                    return;
                }

                this.IsFindButtonVisible = "Collapsed";
            }
        }

        /// <summary>
        /// Gets or sets the visibility of the Find Button
        /// </summary>
        public string IsFindButtonVisible
        {
            get
            {
                return this.isFindButtonVisible;
            }

            set
            {
                this.isFindButtonVisible = value;
                this.NotifyPropertyChanged("IsFindButtonVisible");
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether IsFileTablesDropDownEnabled.
        /// </summary>
        public bool IsFileTablesDropDownEnabled
        {
            get
            {
                return this.isFileTablesDropDownEnabled;
            }

            set
            {
                this.isFileTablesDropDownEnabled = value;
                this.NotifyPropertyChanged("IsFileTablesDropDownEnabled");
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether IsDocumentSelected.
        /// </summary>
        public bool IsDocumentSelected
        {
            get
            {
                return this.isDocumentSelected;
            }

            set
            {
                this.isDocumentSelected = value;

                if (this.isDocumentSelected && this.selectedDocuments.Count == 0)
                {
                    this.isDocumentSelected = false;
                }

                this.NotifyPropertyChanged("IsDocumentSelected");
            }
        }

        /// <summary>
        /// Gets or sets SelectedSearchDocumentProperty.
        /// </summary>
        public string SelectedSearchDocumentProperty
        {
            get
            {
                return this.selectedSearchDocumentProperty;
            }

            set
            {
                this.selectedSearchDocumentProperty = value;
                this.NotifyPropertyChanged("SelectedSearchDocumentProperty");
            }
        }

        /// <summary>
        /// Gets SearchDocumentProperties.
        /// </summary>
        public ObservableCollection<string> SearchDocumentProperties { get; private set; }

        /// <summary>
        /// Gets or sets TagListSelectedItem.
        /// </summary>
        public KeyPhrase TagListSelectedItem
        {
            get
            {
                return this.tagListSelectedItem;
            }

            set
            {
                this.tagListSelectedItem = value;
                this.NotifyPropertyChanged("TagListSelectedItem");
            }
        }

        /// <summary>
        /// Gets or sets SearchText.
        /// </summary>
        public string SearchText
        {
            get
            {
                return this.searchText;
            }

            set
            {
                this.searchText = value;
                this.NotifyPropertyChanged("SearchText");
            }
        }

        /// <summary>
        /// Gets or sets TagGridSelectedItem.
        /// </summary>
        public KeyPhrase TagGridSelectedItem
        {
            get
            {
                return this.tagGridSelectedItem;
            }

            set
            {
                this.tagGridSelectedItem = value;
                this.NotifyPropertyChanged("TagGridSelectedItem");
                if (this.tagGridSelectedItem != null)
                {
                    this.FilterDocumentsByTag(this, new SelectedTagEventArgs(this.tagGridSelectedItem.Name));
                }
            }
        }

        /// <summary>
        /// Gets or sets TxtSearch.
        /// </summary>
        public string TxtSearch
        {
            get
            {
                return this.txtSearch;
            }

            set
            {
                this.txtSearch = value;
                this.NotifyPropertyChanged("TxtSearch");
            }
        }

        /// <summary>
        /// Gets DatabaseTags.
        /// </summary>
        public ObservableCollection<KeyPhrase> DatabaseTags { get; private set; }

        /// <summary>
        /// Gets or sets the datagrid selected document
        /// </summary>
        public Document DocumentsSelectedItem
        {
            get
            {
                return this.selectedDocument;
            }

            set
            {
                this.IsDocumentSelected = value != null;

                if (value == null)
                {
                    this.RelatedDocuments.Clear();
                    this.selectedDocument = null;
                    this.NotifyPropertyChanged("DocumentsSelectedItem");
                }
                else
                {
                    if (value == this.selectedDocument)
                    {
                        return;
                    }

                    this.selectedDocument = value;
                    this.NotifyPropertyChanged("DocumentsSelectedItem");
                    this.GetTags();
                    this.GetRelatedDocuments();
                }
            }
        }

        /// <summary>
        /// Gets TagCloudTags.
        /// </summary>
        public ObservableCollection<KeyPhrase> TagCloudTags
        {
            get
            {
                this.currentTagCloudTags.Clear();
                for (var i = 0; i < this.TopResultsSliderValue; i++)
                {
                    this.currentTagCloudTags.Add(this.fullTagCloudTags[i]);
                }

                return this.currentTagCloudTags;
            }

            private set
            {
                this.fullTagCloudTags = value;
            }
        }

        /// <summary>
        /// Gets or sets Slider maximum value
        /// </summary>
        public double SliderMaxValue
        {
            get
            {
                return this.sliderMax;
            }

            set
            {
                this.sliderMax = Convert.ToInt32(Math.Round(value));
                this.NotifyPropertyChanged("SliderMaxValue");
            }
        }

        /// <summary>
        /// Gets or sets TopResultsSliderValue.
        /// </summary>
        public double TopResultsSliderValue
        {
            get
            {
                return Convert.ToInt32(Math.Round(this.sliderValue));
            }

            set
            {
                this.sliderValue = value;
                this.TagAmountStatus = Convert.ToInt32(Math.Round(this.sliderValue)).ToString(CultureInfo.CurrentCulture);

                this.NotifyPropertyChanged("TopResultsSliderValue");

                this.view.RedrawTagCloud(this.TagCloudTags);
            }
        }

        /// <summary>
        /// Gets or sets Documents.
        /// </summary>
        public ObservableCollection<Document> Documents { get; set; }

        /// <summary>
        /// Gets or sets RelatedDocuments.
        /// </summary>
        public ObservableCollection<Document> RelatedDocuments { get; set; }

        /// <summary>
        /// Gets or sets TagAmountStatus.
        /// </summary>
        public string TagAmountStatus
        {
            get
            {
                return this.amountStatus;
            }

            set
            {
                if (value == this.amountStatus)
                {
                    return;
                }

                this.amountStatus = value;
                this.NotifyPropertyChanged("TagAmountStatus");
            }
        }

        /// <summary>
        /// Gets or sets TagTargetStatus.
        /// </summary>
        public string TagTargetStatus
        {
            get
            {
                return this.targetStatus;
            }

            set
            {
                if (value == this.targetStatus)
                {
                    return;
                }

                this.targetStatus = value;
                this.NotifyPropertyChanged("TagTargetStatus");
            }
        }
        #endregion

        #region Public Methods
        /// <summary>
        /// Finds the selected document in the Documents list and calls a method that updates the view
        /// </summary>
        public void UpdateSelectedDocument()
        {
            foreach (var document in this.Documents.Where(document => document.FileName.Equals(this.SelectedRelatedDocument.FileName)))
            {
                this.view.UpdateDocumentListFocus(document);
            }
        }

        /// <summary>
        /// Method called after a selection in the documentdataGrid has made
        /// </summary>
        /// <param name="selectedItems">
        /// The list of selected records for the document list.
        /// </param>
        public void OnSelectionChanged(object selectedItems)
        {
            var selectedRecords = selectedItems as IList;
            this.selectedDocuments.Clear();
            if (selectedRecords != null)
            {
                foreach (Document currentDoc in selectedRecords)
                {
                    this.selectedDocuments.Add(currentDoc.Guid);
                }

                if (selectedRecords.Count == 1)
                {
                    this.DocumentsSelectedItem = selectedRecords[0] as Document;
                }
                else
                {
                    this.DocumentsSelectedItem = null;
                }
            }

            this.GetTags();
        }

        /// <summary>
        /// Method called after a Tag Filter selection is perfomed
        /// </summary>
        /// <param name="items">
        /// The list of records for the tags
        /// </param>
        public void OnTagFilterSelectionChanged(object items)
        {
            var tagList = items as IList;
            this.tagListSelectedItems.Clear();
            if (tagList == null)
            {
                return;
            }

            foreach (var tag in tagList.Cast<KeyPhrase>().Where(tag => tag.IsSelected))
            {
                this.tagListSelectedItems.Add(tag);
            }

            this.TagListSelectedItem = this.tagListSelectedItems.Count != 0 ? this.tagListSelectedItems[0] : null;
        }

        /// <summary>
        /// Updates the Database name list
        /// </summary>
        public void GetDatabaseNameList()
        {
            this.domainService.GetDatabaseNameList(this.OnGetDatabaseNameListCompleted, this.SelectedSqlInstance, true);
        }

        /// <summary>
        /// Shows the Create FileTable dialog
        /// </summary>
        public void ShowCreateFileTableDialog()
        {
            var fileTableDialog = new FileTablePage(this.SelectedSqlInstance);
            fileTableDialog.Closed += this.FileTableDialogClosed;
            fileTableDialog.Show();
        }

        /// <summary>
        /// Shows the File Information dialog
        /// </summary>
        public void ShowFileInformationDialog()
        {
            var fileInformationDialog = new FileInformationPage(this.DocumentsSelectedItem);
            fileInformationDialog.Show();
        }

        /// <summary>
        /// Shows the Configure Instance dialog
        /// </summary>
        public void ShowConfigureInstanceDialog()
        {
            var instanceDialog = new ConfigureInstancePage();
            instanceDialog.CloseDialogHandler += this.ConfigureInstanceDialogClosed;
            instanceDialog.Show();
        }
        
        /// <summary>
        /// Builds a query made of multiple condition and searches documents that match them
        /// </summary>
        public void SearchDocuments()
        {
            if (this.SelectedSqlInstance == null || this.SelectedDatabase == null || this.SelectedFileTable == null)
            {
                return;
            }

            var query = string.Empty;

            if (!String.IsNullOrEmpty(this.SearchText))
            {
                var saniSearchText = this.SearchText;
                saniSearchText.Replace(';', ' ');
                var test = Application.Current.Resources["FullTextCondition"].ToString();
                query += String.Format(CultureInfo.InvariantCulture, Application.Current.Resources["FullTextCondition"].ToString(), saniSearchText);
            }

            if (!String.IsNullOrEmpty(this.TxtSearch))
            {
                var saniTxtSearch = this.TxtSearch;
                saniTxtSearch.Replace(';', ' ');
                if (!String.IsNullOrEmpty(query))
                {
                    query += Application.Current.Resources["AndDelimiter"].ToString();
                }

                query += String.Format(
                    CultureInfo.InvariantCulture,
                    Application.Current.Resources["DocumentPropertyCondition"].ToString(),
                    this.SelectedSearchDocumentProperty,
                    saniTxtSearch);
            }

            if (this.tagListSelectedItems.Count() > 0)
            {
                if (!String.IsNullOrEmpty(query))
                {
                    query += Application.Current.Resources["AndDelimiter"].ToString();
                }

                var tags = String.Empty;

                foreach (var tag in this.tagListSelectedItems)
                {
                    if (!String.IsNullOrEmpty(tags))
                    {
                        tags += ",";
                    }

                    tags += tag.Name;
                }

                query += String.Format(CultureInfo.InvariantCulture, Application.Current.Resources["TagCondition"].ToString(), tags);
            }

            this.domainService.GetSearchResults(this.OnGetDocumentsCompleted, this.SelectedSqlInstance, this.SelectedDatabase.Name, this.SelectedFileTable, query);
        }

        /// <summary>
        /// Retrieve all documents for the database
        /// </summary>
        public void UpdateDocuments()
        {
            if (this.SelectedDatabase == null || this.SelectedFileTable == null)
            {
                return;
            }

            this.domainService.GetDocuments(this.OnGetDocumentsCompleted, this.SelectedSqlInstance, this.SelectedDatabase.Name, this.SelectedFileTable);
        }

        /// <summary>
        /// Retrieve tags for the database or a document
        /// </summary>
        public void GetTags()
        {
            if (this.SelectedSqlInstance == null || this.SelectedDatabase == null || this.SelectedFileTable == null)
            {
                return;
            }

            if (this.selectedDocuments.Count == 0)
            {
                this.domainService.GetTags(this.OnGetTagsCompleted, this.SelectedSqlInstance, this.SelectedDatabase.Name, this.SelectedFileTable, SliderTopMaxValue);
                this.TagTargetStatus = this.SelectedDatabase.Name;
            }
            else
            {
                this.domainService.GetTagsForDocuments(this.OnGetTagsForDocumentsCompleted, this.SelectedSqlInstance, this.SelectedDatabase.Name, this.SelectedFileTable, this.selectedDocuments, SliderTopMaxValue);
                var count = this.selectedDocuments.Count();
                var selectedName = string.Empty;
                
                foreach (var document in this.Documents.Where(document => document.Guid == this.selectedDocuments.First()))
                {
                    selectedName = document.FileName;
                }

                this.TagTargetStatus = count > 1 ? String.Format(CultureInfo.CurrentCulture, Application.Current.Resources["MultipleDocumentsTagTargetStatus"].ToString(), count) : selectedName;
            }
        }

        /// <summary>
        /// Updates the document list depending on the search properties
        /// </summary>
        public void UpdateDocumentList()
        {
            var query = this.TxtSearch;
            var tag = string.Empty;
            var fullText = this.SearchText;
            this.selectedDocument = null;

            if (this.TagListSelectedItem != null)
            {
                tag = this.TagListSelectedItem.ToString();
            }

            if (!String.IsNullOrEmpty(tag) || !String.IsNullOrEmpty(query) || !String.IsNullOrEmpty(fullText))
            {
                this.SearchDocuments();
            }
            else
            {
                this.UpdateDocuments();
            }
        }

        /// <summary>
        /// Method called from command when downloading a document
        /// </summary>
        public void DownloadDocument()
        {
            if (this.SelectedDatabase == null || this.SelectedFileTable == null)
            {
                return;
            }

            if (this.DocumentsSelectedItem == null)
            {
                return;
            }

            var saveFileDialog = new SaveFileDialog { DefaultExt = "." + this.DocumentsSelectedItem.FileType, Filter = "All Files|*.*" };
            var retval = saveFileDialog.ShowDialog();

            if (retval != true)
            {
                return;
            }

            try
            {
                this.saveFileStream = saveFileDialog.OpenFile();
                this.domainService.DownloadDocument(this.OnDownloadDocumentComplete, this.SelectedSqlInstance, this.SelectedDatabase.Name, this.SelectedFileTable, this.DocumentsSelectedItem.Guid);
            }
            catch (Exception e)
            {
                ShowError(e);
            }
        }

        /// <summary>
        /// Method called from command when deleting a document
        /// </summary>
        public void DeleteDocument()
        {
            if (this.DocumentsSelectedItem == null)
            {
                return;
            }

            this.domainService.DeleteDocument(this.OnDeleteDocumentCompleted, this.SelectedSqlInstance, this.SelectedDatabase.Name, this.SelectedFileTable, this.DocumentsSelectedItem.Guid);
        }

        /// <summary>
        /// Method called from command when uploading a file to the document library
        /// </summary>
        public void UploadFile()
        {
            var dlg = new OpenFileDialog
            {
                Multiselect = false,
                Filter = "All Files|*.*"
            };

            var retval = dlg.ShowDialog();

            if (retval != true)
            {
                return;
            }

            var fileName = dlg.File.Name;

            try
            {
                var fileStream = dlg.File.OpenRead();

                var buffer = new byte[4096];

                using (var memory = new MemoryStream())
                {
                    int bytesRead;
                    while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) != 0)
                    {
                        memory.Write(buffer, 0, bytesRead);
                    }

                    this.domainService.UploadDocument(this.OnDocumentUploaded, this.SelectedSqlInstance, this.SelectedDatabase.Name, this.SelectedFileTable, memory.ToArray(), fileName);
                }
            }
            catch (Exception e)
            {
                var errorDialog = new ErrorDialogPage(e.Message);
                errorDialog.Show();
            }
        }

        /// <summary>
        /// Method called after the Main View has been loaded
        /// </summary>
        /// <param name="sender">
        /// The sender.
        /// </param>
        /// <param name="e">
        /// The event argument.
        /// </param>
        /// <exception cref="NotImplementedException">
        /// </exception>
        public void MainViewLoaded(object sender, RoutedEventArgs e)
        {
            this.ShowConfigureInstanceDialog();
        }

        /// <summary>
        /// Event executed when the Related Document list gains focus
        /// </summary>
        /// <param name="sender">
        /// The sender.
        /// </param>
        /// <param name="e">
        /// The event arguments.
        /// </param>
        public void RelatedDocumentGridGotFocus(object sender, RoutedEventArgs e)
        {
            this.IsDocumentSelected = false;
        }

        /// <summary>
        /// Event executed when the shortcut key for the search button is pressed
        /// </summary>
        /// <param name="sender">
        /// The sender.
        /// </param>
        /// <param name="keyEventArgs">
        /// The key Event Args.
        /// </param>
        public void SearchButtonShortCutPressed(object sender, KeyEventArgs keyEventArgs)
        {
            // check for the specific 'enter' key, then check modifiers
            if (keyEventArgs.Key == Key.Enter)
            {
                this.UpdateDocumentList();
            } 
        }

        /// <summary>
        /// Event executed when the Related Documents list loses focus
        /// </summary>
        /// <param name="sender">
        /// The sender.
        /// </param>
        /// <param name="e">
        /// The event arguments.
        /// </param>
        public void RelatedDocumentGridLostFocus(object sender, RoutedEventArgs e)
        {
            this.IsDocumentSelected = true;
        }

        /// <summary>
        /// Filters the document list when clicking on a tag
        /// </summary>
        /// <param name="sender">
        /// The sender.
        /// </param>
        /// <param name="e">
        /// The routed event param.
        /// </param>
        public void FilterDocumentsByTag(object sender, RoutedEventArgs e)
        {
            var tag = ((SelectedTagEventArgs)e).Tag;

            this.domainService.GetDocumentsByTag(this.OnGetDocumentsByTagCompleted, this.SelectedSqlInstance, this.SelectedDatabase.Name, this.SelectedFileTable, tag);
        }

        /// <summary>
        /// Obtains the documents related to the selected one
        /// </summary>
        public void GetRelatedDocuments()
        {
            if (this.selectedDatabase == null)
            {
                return;
            }

            this.domainService.GetRelatedDocuments(
                this.OnGetRelatedDocumentsCompleted,
                this.SelectedSqlInstance,
                this.SelectedDatabase.Name,
                this.SelectedFileTable,
                    this.DocumentsSelectedItem.Guid);
        }

        #endregion

        #region Callback methods

        /// <summary>
        /// Shows the Information Dialog error
        /// </summary>
        /// <param name="exception">
        /// The exception.
        /// </param>
        private static void ShowError(Exception exception)
        {
            if (exception == null)
            {
                return;
            }

            var errorDialog = new ErrorDialogPage(exception.Message);
            errorDialog.Show();
        }

        /// <summary>
        /// Callback for the GetDocumentsByTag method
        /// </summary>
        /// <param name="documents">
        /// The documents.
        /// </param>
        /// <param name="exception">
        /// The exception.
        /// </param>
        private void OnGetDocumentsByTagCompleted(ObservableCollection<Document> documents, Exception exception)
        {
            if (exception != null)
            {
                ShowError(exception);
                return;
            }

            this.Documents.Clear();

            foreach (var doc in documents)
            {
                this.Documents.Add(doc);
            }

            this.NotifyPropertyChanged("Documents");
        }

        /// <summary>
        /// When the server returns the document, a save as dialog is shown
        /// </summary>
        /// <param name="document">
        /// The document.
        /// </param>
        /// <param name="exception">
        /// The exception.
        /// </param>
        private void OnDownloadDocumentComplete(byte[] document, Exception exception)
        {
            if (exception != null)
            {
                ShowError(exception);
            }
            else if (this.saveFileStream != null)
            {
                this.saveFileStream.Write(document, 0, document.Length);
                this.saveFileStream.Close();
                this.saveFileStream = null;
            }
        }

        /// <summary>
        /// Callback to update the documents list
        /// </summary>
        /// <param name="documents">
        /// The documents.
        /// </param>
        /// <param name="exception">
        /// The exception.
        /// </param>
        private void OnGetDocumentsCompleted(ObservableCollection<Document> documents, Exception exception)
        {
            if (exception != null)
            {
                ShowError(exception);
                return;
            }

            this.Documents.Clear();

            foreach (var doc in documents)
            {
                this.Documents.Add(doc);
            }

            this.NotifyPropertyChanged("Documents");
        }

        /// <summary>
        /// Method called when the GetRelatedDocuments call finishes
        /// </summary>
        /// <param name="documents">
        /// The documents.
        /// </param>
        /// <param name="exception">
        /// The exception.
        /// </param>
        private void OnGetRelatedDocumentsCompleted(ObservableCollection<Document> documents, Exception exception)
        {
            if (exception != null)
            {
                ShowError(exception);
                return;
            }

            this.RelatedDocuments.Clear();

            foreach (var doc in documents)
            {
                this.RelatedDocuments.Add(doc);
            }

            this.NotifyPropertyChanged("RelatedDocuments");
        }

        /// <summary>
        /// Callback that returns all search properties
        /// </summary>
        /// <param name="sps">
        /// The search property list.
        /// </param>
        /// <param name="exception">
        /// The exception.
        /// </param>
        private void OnGetSearchPropertiesCompleted(IEnumerable<string> sps, Exception exception)
        {
            if (exception != null)
            {
                ShowError(exception);
                return;
            }

            this.SearchDocumentProperties.Clear();

            foreach (var sp in sps)
            {
                this.SearchDocumentProperties.Add(sp);
            }

            this.SelectedSearchDocumentProperty = this.SearchDocumentProperties.Last();
        }

        /// <summary>
        /// Callback that returns all databases in a string list
        /// </summary>
        /// <param name="databaseList">
        /// The database list.
        /// </param>
        /// <param name="exception">
        /// The exception.
        /// </param>
        private void OnGetDatabaseNameListCompleted(IEnumerable<string> databaseList, Exception exception)
        {
            if (exception != null)
            {
                ShowError(exception);
                return;
            }

            this.Databases.Clear();

            foreach (var database in databaseList.Select(db => new Database { Name = db }))
            {
                this.Databases.Add(database);
            }

            if (this.Databases.Count() > 0)
            {
                this.SelectedDatabase = this.Databases.First();
            }

            this.NotifyPropertyChanged("Databases");
        }

        /// <summary>
        /// Callback that returns all databases in a string list
        /// </summary>
        /// <param name="filetables">
        /// The filetables.
        /// </param>
        /// <param name="exception">
        /// The exception.
        /// </param>
        private void OnGetFiletableListComplete(IEnumerable<string> filetables, Exception exception)
        {
            if (exception != null)
            {
                ShowError(exception);
                return;
            }

            this.FileTables.Clear();

            foreach (var ft in filetables)
            {
                this.FileTables.Add(ft);
            }

            this.SelectedFileTable = this.FileTables.First();
        }

        /// <summary>
        /// Callback when the service returns tags for a group of documents
        /// </summary>
        /// <param name="keyPhrasesDictionary">
        /// The key Phrases Dictionary.
        /// </param>
        /// <param name="exception">
        /// The exception.
        /// </param>
        private void OnGetTagsForDocumentsCompleted(Dictionary<string, double> keyPhrasesDictionary, Exception exception)
        {
            if (exception != null)
            {
                ShowError(exception);
                return;
            }

            this.fullTagCloudTags.Clear();

            foreach (var nkp in keyPhrasesDictionary.Select(kp => new KeyPhrase { Name = kp.Key, Score = (float)kp.Value }))
            {
                this.fullTagCloudTags.Add(nkp);
            }

            this.TopResultsSliderValue = this.fullTagCloudTags.Count;
            this.SliderMaxValue = this.TopResultsSliderValue;
        }

        /// <summary>
        /// Callback when the service returns all tags
        /// </summary>
        /// <param name="keyPhrasesDictionary">
        /// The key Phrases Dictionary.
        /// </param>
        /// <param name="exception">
        /// The exception.
        /// </param>
        private void OnGetTagsCompleted(Dictionary<string, double> keyPhrasesDictionary, Exception exception)
        {
            if (exception != null)
            {
                ShowError(exception);
                return;
            }

            this.fullTagCloudTags.Clear();

            var checkedTags = new Collection<string>();
            foreach (var tag in this.DatabaseTags.Where(tag => tag.IsSelected))
            {
                checkedTags.Add(tag.Name);
            }

            this.DatabaseTags.Clear();

            foreach (var nkp in keyPhrasesDictionary.Select(kp => new KeyPhrase { Name = kp.Key, Score = (float)kp.Value }))
            {
                this.fullTagCloudTags.Add(nkp);

                this.DatabaseTags.Add(nkp);

                if (!checkedTags.Contains(nkp.Name))
                {
                    continue;
                }

                nkp.IsSelected = true;
            }

            this.TopResultsSliderValue = this.fullTagCloudTags.Count;
            this.SliderMaxValue = this.TopResultsSliderValue;
            this.TagListSelectedItem = this.DatabaseTags.Where(tag => tag.IsSelected).FirstOrDefault();
            this.NotifyPropertyChanged("TagListSelectedItem");
        }

        /// <summary>
        /// Callback when the service uploads a document
        /// </summary>
        /// <param name="returnValue">
        /// The return Value.
        /// </param>
        /// <param name="exception">
        /// The exception.
        /// </param>
        private void OnDocumentUploaded(bool returnValue, Exception exception)
        {
            if (exception != null)
            {
                ShowError(exception);
                return;
            }

            this.UpdateDocumentList();
        }

        /// <summary>
        /// Once the document's deleted, the document list is updated
        /// </summary>
        /// <param name="returnValue">
        /// The return Value.
        /// </param>
        /// <param name="exception">
        /// The exception.
        /// </param>
        private void OnDeleteDocumentCompleted(bool returnValue, Exception exception)
        {
            if (exception != null)
            {
                ShowError(exception);
                return;
            }

            this.UpdateDocuments();
        }

        /// <summary>
        /// Executed when the child window dialog is closed
        /// </summary>
        /// <param name="sender">
        /// The sender.
        /// </param>
        /// <param name="e">
        /// The event argument.
        /// </param>
        private void FileTableDialogClosed(object sender, EventArgs e)
        {
            var dialog = (FileTablePage)sender;

            if (dialog.DialogResult.HasValue && dialog.DialogResult.Value)
            {
                this.GetDatabaseNameList();
            }
        }

        /// <summary>
        /// Executed when the child window dialog is closed
        /// </summary>
        /// <param name="sender">
        /// The sender.
        /// </param>
        /// <param name="e">
        /// The event argument.
        /// </param>
        private void ConfigureInstanceDialogClosed(object sender, EventArgs e)
        {
            var dialog = (ChildWindow)sender;
            var instanceSettings = (SqlInstanceSettingsEventArgs)e;

            if (dialog.DialogResult == true)
            {
                this.SqlInstances.Add(instanceSettings.SqlInstanceName);
                this.SelectedSqlInstance = instanceSettings.SqlInstanceName;
            }
            else
            {
                dialog.Show();
            }
        }

        #endregion

        #region Private Methods
        /// <summary>
        /// Initializes a new instance of the <see cref="MainPageViewModel"/> class.
        /// </summary>
        /// <param name="mainPage">
        /// The main Page.
        /// </param>
        /// <param name="service">
        /// The service.
        /// </param>
        private void Initialize(IView mainPage, IDomainService service)
        {
            this.view = mainPage;
            this.domainService = service;
            this.selectedRelatedDocument = null;
            this.Documents = new ObservableCollection<Document>();
            this.RelatedDocuments = new ObservableCollection<Document>();
            this.SqlInstances = new ObservableCollection<string>();
            this.DatabaseTags = new ObservableCollection<KeyPhrase>();
            this.TagCloudTags = new ObservableCollection<KeyPhrase>();
            this.currentTagCloudTags = new ObservableCollection<KeyPhrase>();
            this.tagListSelectedItems = new ObservableCollection<KeyPhrase>();
            this.Databases = new ObservableCollection<Database>();
            this.FileTables = new ObservableCollection<string>();
            this.SearchDocumentProperties = new ObservableCollection<string>();
            this.selectedDocument = null;
            this.selectedDocuments = new List<string>();
        }
        #endregion

        #region Notify Property Changed
        /// <summary>
        /// Notify property Changed
        /// </summary>
        /// <param name="info">
        /// The info string.
        /// </param>
        private void NotifyPropertyChanged(string info)
        {
            if (this.PropertyChanged != null)
            {
                this.PropertyChanged(this, new PropertyChangedEventArgs(info));
            }
        }
        #endregion
    }
}
